package com.uoko.common.reflection;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *
 * </p>
 *
 * @author chenyingchun
 * Email qiaohai@uoko.com
 * created by 2018/6/6 0006
 */
public class BeanUtil {
    /**
     * Bean信息集合，用来做缓存。
     */
    private static Map<Class<?>, BeanInfo> beanInfos = new HashMap<>();

    /**
     * 各个Bean的属性map集合,也是用来做缓存。
     */
    private static Map<Class<?>, Map<String, PropertyDescriptor>> propertys = new HashMap<>();

    /**
     * 注入到缓存
     *
     * @param beanClass
     * @return
     * @throws IntrospectionException
     */
    private static synchronized BeanInfo putBeanInfo(Class<?> beanClass) throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        beanInfos.put(beanClass, beanInfo);
        propertys.put(beanClass, getBeanPropertys(beanInfo));
        return beanInfo;
    }

    /**
     * 注入到缓存
     *
     * @param beanClass
     * @return
     * @throws IntrospectionException
     */
    private static synchronized Map<String, PropertyDescriptor> putPropertyMap(Class<?> beanClass)
            throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        beanInfos.put(beanClass, beanInfo);
        Map<String, PropertyDescriptor> propertyMap = getBeanPropertys(beanInfo);
        propertys.put(beanClass, propertyMap);
        return propertyMap;
    }

    /**
     * 传入一个class获取beanInfo
     *
     * @param beanClass
     * @return
     * @throws IntrospectionException
     */
    public static BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
        BeanInfo beanInfo = beanInfos.get(beanClass);
        if (beanInfo == null)
            return putBeanInfo(beanClass);
        return beanInfo;
    }

    /**
     * 传入一个beanInfo获取属性集合
     *
     * @param beanInfo
     * @return
     */
    public static Map<String, PropertyDescriptor> getBeanPropertys(BeanInfo beanInfo) {
        if (beanInfo == null)
            return null;
        Map<String, PropertyDescriptor> properMap = new HashMap<>();
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            // 过滤class属性
            if (!"class".equals(key)) {
                properMap.put(key, property);
            }
        }
        return properMap;
    }

    /**
     * 传入一个class获取属性集合
     *
     * @param beanClass
     * @return
     * @throws IntrospectionException
     */
    public static Map<String, PropertyDescriptor> getBeanPropertys(Class<?> beanClass) throws IntrospectionException {
        Map<String, PropertyDescriptor> propertyMap = propertys.get(beanClass);
        if (propertyMap == null)
            return putPropertyMap(beanClass);
        return propertyMap;
    }

    /**
     *
     * @param beanClass
     * @param name
     * @return
     * @throws IntrospectionException
     */
    public static PropertyDescriptor getBeanProperty(Class<?> beanClass, String name) throws IntrospectionException {
        Map<String, PropertyDescriptor> propertyMap = getBeanPropertys(beanClass);
        if (propertyMap != null)
            return propertyMap.get(name);
        else
            return null;
    }

    /**
     *
     * @param beanClass
     * @return
     * @throws IntrospectionException
     */
    public static PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass) throws IntrospectionException {
        BeanInfo beanInfo = getBeanInfo(beanClass);
        if (beanInfo != null)
            return beanInfo.getPropertyDescriptors();
        return null;
    }

    /***
     *
     * @return
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws IntrospectionException
     */
    public static Object getPropertyValue(Object obj, String name)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException {
        if (obj == null || name == null || name.isEmpty())
            return null;
        PropertyDescriptor property = getBeanProperty(obj.getClass(), name);
        Method getter = property.getReadMethod();
        return getter.invoke(obj);
    }

    /***
     *
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void setPropertyValue(Object obj, String name, Object value)
            throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (obj == null || name == null || name.isEmpty())
            return;
        PropertyDescriptor property = getBeanProperty(obj.getClass(), name);
        Method setter = property.getWriteMethod();
        setter.invoke(obj, value);
    }

    /***
     *
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void bean2Bean(Object soure, Object target)
            throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (soure == null || target == null)
            return;
        PropertyDescriptor[] aPropertys = getPropertyDescriptors(soure.getClass());
        Map<String, PropertyDescriptor> bPropertyMap = getBeanPropertys(target.getClass());
        for (PropertyDescriptor aProper : aPropertys) {
            String key = aProper.getName();
            if (!"class".equals(key)) {
                PropertyDescriptor bProper = bPropertyMap.get(key);
                if (bProper != null) {
                    Method getter = aProper.getReadMethod();
                    Method setter = bProper.getWriteMethod();
                    setter.invoke(target, getter.invoke(soure));
                }
            }
        }
    }

    /**
     *
     * @param names
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void bean2Bean(Object source, Object target, String[] names)
            throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (source == null || target == null || names == null || names.length == 0)
            return;
        Map<String, PropertyDescriptor> aPropertyMap = getBeanPropertys(source.getClass());
        Map<String, PropertyDescriptor> bPropertyMap = getBeanPropertys(target.getClass());
        for (String key : names) {
            PropertyDescriptor aProper = aPropertyMap.get(key);
            PropertyDescriptor bProper = bPropertyMap.get(key);
            if (aProper != null && bProper != null) {
                Method getter = aProper.getReadMethod();
                Method setter = bProper.getWriteMethod();
                setter.invoke(target, getter.invoke(source));
            }
        }
    }

    /***
     *
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void bean2Map(Object source, Map<String, Object> target)
            throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (source == null || target == null)
            return;
        PropertyDescriptor[] aPropertys =
                // Introspector.getBeanInfo(s.getClass()).getPropertyDescriptors();
                getPropertyDescriptors(source.getClass());
        for (PropertyDescriptor aProper : aPropertys) {
            String key = aProper.getName();
            if (!"class".equals(key)) {
                Method getter = aProper.getReadMethod();
                target.put(key, getter.invoke(source));
            }
        }
    }

    /***
     *
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void bean2Map(Object source, Map<String, Object> target, String[] names)
            throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (source == null || target == null || names == null || names.length == 0)
            return;
        Map<String, PropertyDescriptor> aPropertyMap = getBeanPropertys(source.getClass());
        for (String key : names) {
            PropertyDescriptor aProper = aPropertyMap.get(key);
            if (aProper != null) {
                Method getter = aProper.getReadMethod();
                target.put(key, getter.invoke(source));
            }
        }
    }

    /***
     *
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void map2Bean(Map<String, Object> source, Object target)
            throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (source == null || source.isEmpty() || target == null)
            return;
        PropertyDescriptor[] bPropertys = getPropertyDescriptors(target.getClass());
        for (PropertyDescriptor bProper : bPropertys) {
            String key = bProper.getName();
            if (!"class".equals(key) && source.containsKey(key)) {
                Object value = source.get(key);
                Method setter = bProper.getWriteMethod();
                setter.invoke(target, value);
            }
        }
    }

    /***
     *
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void map2Bean(Map<String, Object> source, Object target, String[] names)
            throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (source == null || source.isEmpty() || target == null || names == null || names.length == 0)
            return;
        Map<String, PropertyDescriptor> bPropertyMap = getBeanPropertys(target.getClass());
        for (String key : names) {
            PropertyDescriptor bProper = bPropertyMap.get(key);
            if (bProper != null && source.containsKey(key)) {
                Object value = source.get(key);
                Method setter = bProper.getWriteMethod();
                setter.invoke(target, value);
            }
        }

    }
}
